home *** CD-ROM | disk | FTP | other *** search
-
- /*
- * scheduler.c
- *
- * Implementation of shared scheduler class. All process
- * objects interact with the scheduler object trying to
- * getreadythreads off the ready queue.
- *
- * ThreadPool trivially defines the base class for the shared
- * ready thread pool.
- *
- * ThreadPoolQueue defines the default scheduling discipline.
- * Very simple, no priorities.... nothing fancy.
- *
- * Last modified: 1/1/88
- * by: bnb
- * reason: take care for preschedthreads
- *
- * Last modified: 12/21/87
- * by: bnb
- * reason: add these comments
- *
- */
-
- #define _SCHEDULER_C
-
- #include <sys/types.h>
- #include <sys/signal.h>
- #include <osfcn.h>
- #include <stream.h>
- #include "presto.h"
-
-
-
- //
- // preschedthreads queue is where threads that get created
- // before the scheduler exists get placed. On scheduler
- // creation, these guys are moved to the real thread pool.
- // This allows threads to be created in static constructors without
- // requiring that the entire system be in place.
- //
-
-
- shared_t ThreadQ *preschedthreads = 0;
-
-
- //
- // Definition of default ThreadPool{Queue} type known to scheduler.
- //
-
-
- ThreadPool::ThreadPool()
- {
- }
-
- ThreadPool::~ThreadPool()
- {
- }
-
- Thread*
- ThreadPool::get()
- {
- error("Base threadpool get");
- // NOTREACHED
- return 0;
- }
-
- void
- ThreadPool::insert(Thread* t)
- {
- error("Base threadpool get");
- // NOTREACHED
- t = t; // Satisfy cfront
- }
-
- int
- ThreadPool::size()
- {
- error("Base threadpool size");
- // NOTREACHED
- return 1;
- }
-
-
-
- ThreadPoolQueue::ThreadPoolQueue()
- {
- tp_tq = new ThreadQ(TS_READY);
- }
-
- ThreadPoolQueue::~ThreadPoolQueue()
- {
- delete tp_tq;
- }
-
-
- Thread*
- ThreadPoolQueue::get()
- {
- register ThreadQ* tq = tp_tq; // speed hack
- return tq->get();
- }
-
- void
- ThreadPoolQueue::insert(Thread* t)
- {
- register ThreadQ* tq = tp_tq;
- tq->append(t);
- }
-
- int
- ThreadPoolQueue::size()
- {
- return tp_tq->length();
- }
-
-
-
-
- //
- // The main scheduler system
- //
-
- Scheduler::Scheduler(int numschedulers, int quantum = DEFQUANTUM)
- {
- #ifdef PREEMPT
- void sigpreempt_init();
- #endif PREEMPT
-
- sc_p_numschedulers = numschedulers;
- //
- // nullify the threads
- //
- sc_t_ready = new ThreadPoolQueue;
-
- //
- // Only create a process context if one doesn't already exist.
- // User can create his own in Main::init(). Allows us to
- // virtualize the constructor.
- //
- if (thisproc == 0)
- sc_p_procs[0] = new Process(P_ROOT, 0);
- else
- sc_p_procs[0] = thisproc;
-
- sc_p_activeschedulers = 1;
- sc_p_busybits = 0x0;
- sc_lock = new Spinlock;
- sc_quantum = quantum;
-
- #ifdef PREEMPT
- if (sc_quantum)
- sigpreempt_init();
- #endif PREEMPT
- }
-
- //
- // Scheduler invocation function.
- // Create sc_p_numschedulers threads and bind each one of them
- // to a process invocation. Schedule those threads to start running
- // which will in turn run a new scheduler on some new processor.
- //
- // In case we have any preschedthreads, schedule them here.
- //
-
- int
- Scheduler::invoke()
- {
- extern int cpus_online();
- extern void strcpy(char*, char*);
- int cpusonline = cpus_online();
- int pid;
-
- #ifdef PREEMPT
- extern void sigpreempt_beginclock(struct timeval *q);
- extern int preemption_enabled;
- #endif PREEMPT
-
- if (sc_p_numschedulers < 0)
- return sc_p_activeschedulers;
-
- if (sc_p_numschedulers > cpusonline)
- sc_p_numschedulers = cpusonline;
-
- //
- // Initialize the process pool
- //
-
- initsighandlers(0);
-
- for (pid = sc_p_activeschedulers; pid < sc_p_numschedulers; pid++) {
-
- #define XNAME "proc_X"
- char *pname = new char[sizeof(XNAME)];
- strcpy(pname, XNAME);
- pname[5] = pid + 'a' - 1;
- sc_p_procs[pid] = thisproc->newprocess(pname,pid+thisproc->id());
- if (sc_p_procs[pid]->state() & S_ERROR) {
- perror("Scheduler::new Process");
- sc_p_numschedulers = --pid;
- this->abort(SIGKILL);
- kill(getpid(), SIGILL);
- // not reached
- }
- sc_p_busybits |= 1<<pid; // assume busy
- }
-
- initsighandlers(1); // prepare parent to clean up
- #ifdef PREEMPT
- if (sc_quantum) {
- struct timeval q;
- q.tv_sec = sc_quantum / 1000; // in msecs
- q.tv_usec = (sc_quantum % 1000) * 1000; // 1msec = 1000usec
- sigpreempt_beginclock(&q);
- }
- #endif PREEMPT
-
- //
- // take care of threads created in the prescheduler era
- //
-
- if (preschedthreads) {
- Thread *t;
- while (t=preschedthreads->get())
- resume(t);
- delete preschedthreads;
- preschedthreads = 0;
- }
-
- return (sc_p_activeschedulers = sc_p_numschedulers);
- }
-
-
- Scheduler::~Scheduler()
- {
- delete sc_t_ready;
- }
-
-
- void
- Scheduler::halt()
- {
- int pid;
-
- #ifdef PREEMPT
- extern void sigpreempt_stopclock();
-
- if (sc_quantum)
- sigpreempt_stopclock();
- #endif PREEMPT
-
- /*
- * Stop listening for dead children -- otherwise can race with "master"
- * and get spurious "exit" messages from schedulerReapChild().
- */
-
- initsighandlers(-1); // turn off SIGCHLD
-
- /*
- * Ask the kids to quietly die.
- */
-
- for (pid = 1; pid < sc_p_numschedulers; pid++)
- sc_p_procs[pid]->request( R_RETURN /*R_DIE*/);
-
- sc_p_procs[0]->request(R_RETURN); // cause main to return
- }
-
-
-
- //
- // Resume a thread within a process.
- // If this is a virgin thread, then the thread code starts off a little
- // but differently. See threads.c
- //
-
- void
- Scheduler::resume(Thread *t)
- {
- if (t->flags()&TF_SCHEDULER)
- t->error("Can't resume a scheduler thread\n");
- t->isready();
- sc_t_ready->insert(t); // some process should grab me
- }
-
-
-
-
- //
- // Have to serialize access here so we can know when the system stops.
- // Should really be more sophisticated as to determining when everything
- // is done.
- //
-
- Thread*
- Scheduler::getreadythread()
- {
- sc_lock->lock();
-
- Thread *t = sc_t_ready->get();
-
- if (t) {
- (void)busybits(1);
- sc_lock->unlock();
- } else {
- int busy = busybits(0);
- sc_lock->unlock();
- //
- // If no busy procs, and we are the master....
- //
- if (busy == 0 && thisproc->isroot() )
- this->halt();
- }
- return t;
- }
-
-
- void
- Scheduler::error(char *s)
- {
- cerr << "Scheduler error " << s << "\n" << " in " << hex(long(this)) << "\n";
- fatalerror();
- }
-
-
- ThreadPool*
- Scheduler::getreadypool()
- {
- return sc_t_ready;
- }
-
-
- //
- // swap new ready pool. Order of switch is important here.
- // First, replace old pool with new pool.
- // Then, move any threads from old pool to new pool
- // Them, delete old pool.
- //
-
- ThreadPool*
- Scheduler::setreadypool(ThreadPool* newtp)
- {
- ThreadPool* oldpool ;
- Thread* t;
-
- if (!newtp)
- error("Bad arg to newreadypool");
-
- oldpool = sc_t_ready; // can still be active
- sc_t_ready = newtp;
- // we expect to have only reference to
- // oldpool by this point.
- if (oldpool) {
- while (t=oldpool->get())
- sc_t_ready->insert(t);
- }
-
- return oldpool;
- }
-
- void
- Scheduler::print(ostream& s)
- {
- int i;
-
- s << form("(Scheduler):0x%x, sc_t_ready=0x%x, ", this, sc_t_ready);
- s << form("sc_p_activescheduler=%d, sc_p_busybits=%d, sc_quantum=%d",
- sc_p_activeschedulers, sc_p_busybits, sc_quantum);
- s << sc_lock << "\n";
- for (i = 0; i < sc_p_activeschedulers; i++)
- s << sc_p_procs[i] << "\n";
- }
-
- //
- // global version of abort. If we are the scheduler, then knock
- // everyone else off first, otherwise just knock ourselves off and
- // reply on the scheduler to pick us up.
- //
- int
- abort()
- {
- if (sched) {
- sched->abort(SIGILL);
- // NOT REACHED
- } else
- kill(getpid(), SIGILL); // core dump
- return 0; // not reached
- }
-
-